
<template>
    <div class="hk_video_player_box" ref="playerBox">
        <div  v-for="(item , index) in pageData.urlEncoded"
              :key="item"
              v-loading="loading[index]"
             :style="pageData.videoStyle"
             >
            <!-- 加载工具栏-->
            <div :id="item" class="player_item" ></div>

            <div class="error" v-if="staus[item] >= retry ">
                <img src="./images/error-video.png">
                视频加载失败
            </div>

            <!-- 加载工具栏-->
            <div :style="{ bottom:props.options.showTitle?'0.3rem':0}" class="controller_box">
                <div :key="controller"
                     class="controller_item"
                     v-for="controller in pageData.controller">

                    <Recorder :index="index"
                              :options="pageData"
                              :ref="el=>controllerRefs[`recorder${index}`] = el"
                              name="视频录制"
                              v-if="controller === 'recorder'"/>

                    <Screenshot :index="index"
                                :options="pageData"
                                name="视频截图"
                                v-if="controller === 'screenshot'"/>

                    <Call :index="index"
                          :options="pageData"
                          name="语音对讲"
                          v-if="controller === 'call'"/>

                    <Playback :index="index"
                              :options="pageData"
                              name="视频回放"
                              v-if="controller === 'playback'"/>

                    <Refresh :index="index"
                             :options="pageData"
                             @refresh="clickRefresh"
                             name="刷新"
                             v-if="controller === 'refresh'"/>

                    <Volume :index="index"
                            :options="pageData"
                            name="静音"
                            v-if="controller === 'volume'"/>

                    <FullScreen :index="index"
                                :options="pageData"
                                name="全屏"
                                @callback="fullScreenCallback"
                                v-if="controller === 'fullScreen'"/>

                </div>
            </div>

            <div class="player_name_box" v-if="props.options.showTitle && pageData.PlayUrlItem.length">
                {{pageData.PlayUrlItem[index].AreaName}}- {{pageData.PlayUrlItem[index].CameraName}}
            </div>

        </div>
    </div>
</template>

<script setup>

    import server from "@hook/httpServer/httpServer"

    import {useSetState} from 'vue-hooks-plus'

    import Screenshot from './controller/screenshot'
    import Recorder from './controller/recorder'
    import Call from './controller/call'
    import Playback from './controller/playback'
    import Refresh from './controller/refresh'
    import Volume from './controller/volume'
    import FullScreen from './controller/fullScreen'



    const props = defineProps({
        options: {
            type: Object,
            default: () => new Object(),
            required: true
        }
    })

    const loading = reactive([])

    const playerBox = ref(null)

    // 缓存子组件的引用ref队列
    const controllerRefs = ref({})

    // 默认 video 配置
    const defaultOptions = {
        autoplay: true, // 自动播放
        mutex: false, // 关闭互斥
        live: true, // 开启视频直播模式
        hotkey: false,
        chromecast: true,
        preventClickToggle: true,  // 阻止点击视频时切换播放状态
        screenshot: true,  // 开启截图
    }

    // 默认工具栏配置
    const defaultController = [
        'recorder',  // 视频录制
        'screenshot',  // 视频截图
        'call',  // 语音对讲
        'playback',  // 视频回放
        'refresh',  // 刷新
        'volume',  // 静音
        'fullScreen',  // 全屏
    ]

    // 记录视频队列的加载状态
    const staus = reactive({})

    // 失败后重试次数
    const retry = ref(3)

    // 设定 pageData 缓存组件内全部响应式变量
    const [pageData, setPageData] = useSetState({

        // 当前视频编码队列
        urlEncoded: [],

        // 当前视频详细字段队列
        PlayUrlItem: [],

        // 动态样式计算
        videoStyle: {},

        // 当前视频队列的默认工具栏配置
        controller: [],

        // 当前视频实例化队列
        instance: [],

        // 当前正在进行录屏的video 对象
        recordRTC: null
    })


    watch(pageData.value.urlEncoded , (val)=>{
        val.forEach(item=>{
            if(!staus[item]){
                staus[item] = 0
            }
        })
    })



    // 组件内值修改
    const setData = (data) => {
        setPageData({...pageData.value, ...data})
    }

    // 获取视频请求编码
    const getMonitors = () => {
        const {url, size} = props.options
        const params = {
            MonitorStatus: 1,
            PagerInfo: {PageSize: size, PageIndex: 1}
        }
        return  $HTTP.post(url, {...params}).then(res => {
           return  res.Data.map(item => item.PlayUrl)
        })
    }

    // 初始化视频配置
    const setVideoOptions = (encoded , urlItem , index) => {
        const playerItem = {
            ...defaultOptions,
            container: document.getElementById(encoded)
        }
        playerItem.video = {
            url: urlItem?.PlayUrl || null,
            //url: ' https://cdn-static.m-m10010.com/demo/hls/output.m3u8',
            type: 'customHls',
            customType: {
                customHls: (video, player) => {
                    const hls = new Hls()
                    hls.loadSource(video.src)
                    hls.attachMedia(video)


                    hls.config.maxBufferLength = 10; // 设置最大缓冲区长度为30秒
                    hls.config.liveSyncBuffer = 10;

                    hls.on(Hls.Events.ERROR, (event, data) => {
                        // 如果发生网络错误，则重试
                        if(Hls.ErrorTypes.NETWORK_ERROR === data.type ){
                            if(staus[encoded] < retry.value){
                                setTimeout(()=>{
                                    staus[encoded] += 1
                                    console.log(`窗口${index},编码${encoded} , 重试第${staus[encoded]}次` )
                                    refresh(index)
                                }, 5000)
                            }else{
                                pageData.value.instance[index].destroy()
                            }
                        }
                    })
                }
            }
        }

        const dp = new DPlayer(playerItem)

        updateWatermark(dp)

        let video = document.querySelector("video")

        let videoPlay = video.play();

        videoPlay.then(() => {

        }).catch((err) => {
            setTimeout(() => {
                dp?.volume(0, true, true)
                dp?.play()
            }, 500)
        })

        return dp

    }

    // 添加水印， 如果props.watermark 为空，则不展示水印
    const updateWatermark=(dp)=> {

        if(!props.options.watermark){
            return false
        }

        // 清除可能存在的旧水印
        dp.container.querySelectorAll('.watermark').forEach(el => el.remove());
        // 创建水印容器
        const watermarkContainer = document.createElement('div');
        watermarkContainer.classList.add('watermark-container');
        watermarkContainer.style.position = 'absolute';
        watermarkContainer.style.top = '0';
        watermarkContainer.style.left = '0';
        watermarkContainer.style.width = '100%';
        watermarkContainer.style.height = '100%';
        watermarkContainer.style.pointerEvents = 'none';
        watermarkContainer.style.display = 'flex';
        watermarkContainer.style.justifyContent = 'space-between';
        watermarkContainer.style.alignItems = 'space-between';
        watermarkContainer.style.flexWrap = 'wrap';
        watermarkContainer.style.overflow = 'hidden';

        // 计算每个水印的大小和数量
        const videoWidth = dp.video.clientWidth;
        const videoHeight = dp.video.clientHeight;
        const watermarkSize = 100; // 假设每个水印大小为100px
        const watermarksPerRow = Math.floor(videoWidth / watermarkSize);
        const watermarksPerColumn = Math.floor(videoHeight / watermarkSize);

        // 创建水印元素
        for (let i = 0; i < watermarksPerRow * watermarksPerColumn; i++) {
            const watermark = document.createElement('div');
            watermark.classList.add('watermark');
            watermark.style.width = `${watermarkSize}px`;
            watermark.style.height = `${watermarkSize}px`;
            watermark.style.lineHeight = `${watermarkSize}px`;
            watermark.style.textAlign = 'center';
            watermark.style.color = 'rgba(255, 255, 255, 0.5)';
            watermark.style.fontSize = '.14rem';
            watermark.style.transform = 'rotate(-25deg)';
            watermark.textContent = props.options.watermark;
            watermarkContainer.appendChild(watermark);
        }
        dp.container.appendChild(watermarkContainer)
    }

    // 获取视屏请求拉流地址
    const getMonitorsUrl = (params) => {
        return $HTTP.post(server.video.videoUrlToM3u8, {...params}).then(res => {
            return res.Data
        })
    }

    // 全屏事件的回调
    const fullScreenCallback = (dp)=>{
        updateWatermark(dp)
    }


    // 定义一个数组取交集的方法，用来动态生成工具栏
    const getIntersection = (arr1, arr2) => {
        return arr1.filter(item => arr2.includes(item));
    }

    // 手动刷新
    const clickRefresh=(refreshIndex , code)=>{

       let encoded = pageData.value.urlEncoded[refreshIndex]
        if(encoded){
            staus[encoded] = 0
        }

        refresh(refreshIndex , code)
    }

    // 刷新 , index 表示刷新当前第几个窗口 ， code 表示是否加载新的数据源
    const refresh = async (refreshIndex , code) => {
        let encoded = ''
        if(code){
            encoded = code
            // 先更新编码 ， 再去执行渲染视频操作
            const urlEncoded =  [...pageData.value.urlEncoded]
            urlEncoded[refreshIndex] = encoded
            setData({urlEncoded})
        }else{
            encoded = pageData.value.urlEncoded[refreshIndex]
        }
        loading[refreshIndex] = true

        await nextTick();

        const urlLoad = await getMonitorsUrl({PlayUrl: encoded})

        const instance = await setVideoOptions(encoded , urlLoad ,refreshIndex)

        const PlayUrlItem =  [...pageData.value.PlayUrlItem]
        const instances =  [...pageData.value.instance]

        PlayUrlItem[refreshIndex] = urlLoad
        instances[refreshIndex] = instance

        loading[refreshIndex] = false

        setData({
            PlayUrlItem: PlayUrlItem,
            instance: instances,
        })

    }

    // 初始化渲染视频请求
    const initPlayer= async ()=>{
        // 批量

        // 请求视频编码队列
        const encoded  = await getMonitors()
        setData({urlEncoded: encoded})

        await nextTick();

        const getUrlLoad = encoded.map((item , index)=>{
            loading[index] = true
            return getMonitorsUrl({PlayUrl: item})
        })

        // 请求视频播放地址
        Promise.all([...getUrlLoad]).then(res => {
           const instance = encoded.map((item , index)=>{
               const dp = setVideoOptions(item , res[index] , index )
               loading[index] = false
               return dp
            })

            setData({PlayUrlItem: res , instance})
        })
    }

    // 向父组件暴露内部属性
    defineExpose({refresh , $el:playerBox})

    // 初始化
    onMounted(() => {
        // 设置video style
        const {size = 4, controller = []} = props.options

        const videoStyle = {
            width: 100 / Math.sqrt(size) + '%',
            height: 100 / Math.sqrt(size) + '%'
        }

        const intersection = getIntersection(controller, defaultController);

        setData({
            videoStyle,
            controller: intersection
        })

        initPlayer()
    })

</script>

<style scoped lang="less">
    .hk_video_player_box {
        width: 100%;
        height: 100%;
        display: flex;
        flex-wrap: wrap;
       // background-color: ;

        > div {
            position: relative;
            border: .02rem solid rgba(0, 0, 0, 0.8);
            overflow: hidden;

            > .player_item {
                width: 100%;
                height: 100%;

                ::v-deep {
                    .dplayer-controller-mask,
                    .dplayer-menu ,
                    .dplayer-controller {
                        display: none !important;
                    }

                    // 使用绝对定位 ， 避免盒子被撑开
                    video {
                        position: absolute;
                        left: 0;
                        top:0;
                        right:0;
                        bottom:0;
                        object-fit: fill;
                    }
                }
            }

            .error {
                position: absolute;
                right: 0;
                top: 0;
                bottom:0;
                left: 0;
                background-color: rgba(0, 0, 0, 0.8);
               // background-image: url("./images/error-video.png");
                //background-repeat: no-repeat;
                color: #fff;
                display: flex;
                flex-direction: column;
                justify-content: center;
                align-items: center;
                > img {
                    width: 1.4rem;
                    height: 1.4rem;
                    margin-bottom: .1rem;
                }
            }

            .controller_box {
                position: absolute;
                right: 0;
                top: 0;
                background-color: rgba(0, 0, 0, 0.3);
                min-width: .4rem;
                //display: flex;
                display: none;
                flex-direction: column;
                flex-wrap: wrap;
                direction: rtl;

                .controller_item {
                    width: .4rem;
                    height: .4rem;
                    text-align: center;
                    line-height: .4rem;
                    font-size: .24rem;
                    color: #fff;

                    ::v-deep {
                        .arco-icon {
                            cursor: pointer;

                            &:hover {
                                font-size: .26rem;
                                color: aqua;
                            }
                        }
                    }
                }
            }

            .player_name_box {
                position: absolute;
                display: none;
                right: 0;
                left: 0;
                bottom: 0;
                height: .3rem;
                line-height: .3rem;
                background-color: rgba(255, 255, 255, 0.3);
                color: #fff;
                font-size: .14rem;
                font-weight: bold;
                padding: 0 .3rem;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: normal;
            }


            &:hover {
                .controller_box, .player_name_box {
                    display: flex;
                }
            }

        }
    }
</style>